package com.jt.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 *  @Aspect 我是一个AOP切面类
 *  @Component 将类交给spring容器管理
 */

//@Aspect
//@Component
public class CacheDemoAOP {
    //公式 = 切入点表达式 + 通知方法
    /**
     * 切入点表达式的使用说明
     * 粗粒度：
     *      1.bean（bean的ID，即类名且首字母小写）
     *      2，within（包名.类名）
     * 细粒度：
     *      1,execution
     *      2,annotation
     */
    @Pointcut("bean(itemCatServiceImpl)")
    //@Pointcut("within(com.jt.com.jt.service..*)")  //匹配多级目录
    //@Pointcut("execution(* com.jt.com.jt.service..*.*(..))") //方法参数级别
    public void pointCut(){
        //定义切入点表达式 只为了占位
    }

    /**
     * 定义前置通知，与切入点表达式进行绑定，注意绑定的是方法
     * 区别：pointCut()表示切入点表达式的引用，适用于多个通知同用切入点的情况
     *      bean(itemCatServiceImpl)适用于单个通知，不需要复用的地方
     */

    /**
     * 需求:获取目标对象的相关信息.
     *      1.获取目标方法的路径    包名.类名.方法名
     *      2.获取目标方法的类型  class
     *      3.获取传递的参数
     *      4.记录当前的执行时间
     */
    @Before("pointCut()")
    //@Before("bean(itemCatServiceImpl)")
    public void before(JoinPoint joinPoint){
        //System.out.println("我是一个前置通知。。。");
        Class className = joinPoint.getSignature().getDeclaringType();
        String methodName = joinPoint.getSignature().getName();
        Class<?> targetName = joinPoint.getTarget().getClass();
        Object[] args = joinPoint.getArgs();
        long currentTimeMillis = System.currentTimeMillis();
        System.out.println("方法路径："+className +"."+ methodName);
        System.out.println("目标对象类型："+targetName);
        System.out.println("参数："+ Arrays.toString(args));
        System.out.println("开始时间："+currentTimeMillis+"毫秒");
    }
    @After("pointCut()")
    public void after(){
        System.out.println("这是一个后置通知after");
    }
    @AfterReturning("pointCut()")
    public void afterReturnning(){
        System.out.println("这是一个后置通知afterReturnning");
    }

    /**
     * 环绕通知说明
     * 注意事项:
     *  1.环绕通知中必须添加参数ProceedingJoinPoint
     *  2.ProceedingJoinPoint只能环绕通知使用
     *  3.ProceedingJoinPoint如果当做参数 则必须位于参数的第一位
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        System.out.println("环绕通知开始");
        Object result=null;
        //执行下一个通知或者目标方法
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕通知结束");
        return result;
    }
}
